
GetPlainMapRotation(include_stacker)
{
	if(!isDefined(include_stacker)) include_stacker = true;
	return GetMapRotation(false, false, include_stacker);
}

GetRandomMapRotation(include_stacker)
{
	if(!isDefined(include_stacker)) include_stacker = true;
	return GetMapRotation(true, false, include_stacker);
}

GetCurrentMapRotation()
{
	return GetMapRotation(false, true, false);
}

GetPlayerBasedMapRotation()
{
	return GetMapRotation(false, false, false);
}

GetRandomPlayerBasedMapRotation()
{
	return GetMapRotation(true, false, false);
}

GetMapRotation(random, current, include_stacker)
{
	maprot = "";

	if(current) maprot = strip(getcvar("sv_maprotationcurrent"));

	if(maprot == "")
	{
		if(level.ex_pbrotate || level.ex_mapvotemode == 2 || level.ex_mapvotemode == 3)
		{
			players = level.players;
			if (players.size >= level.ex_mapsizing_large) maprot = strip(getcvar("scr_large_rotation"));
				else if (players.size >= level.ex_mapsizing_medium) maprot = strip(getcvar("scr_med_rotation"));
					else maprot = strip(getcvar("scr_small_rotation"));
		}
		else
		{
			if(include_stacker) maprot = extreme\_ex_maps::reconstructMapRotation();
				else maprot = strip(getcvar("sv_maprotation"));
		}
	}

	if(maprot == "") return undefined;
	
	j = 0;
	temparr2[j] = "";	
	for(i = 0; i < maprot.size; i++)
	{
		if(maprot[i] == " ")
		{
			j++;
			temparr2[j] = "";
		}
		else temparr2[j] += maprot[i];
	}

	temparr = [];
	for(i = 0; i < temparr2.size; i++)
	{
		element = strip(temparr2[i]);
		if(element != "") temparr[temparr.size] = element;
	}

	xmaps = [];
	lastexec = undefined;
	lastgt = getcvar("g_gametype");

	for(i = 0; i < temparr.size;)
	{
		switch(temparr[i])
		{
			case "exec":
				if(isdefined(temparr[i+1])) lastexec = temparr[i+1];
				i += 2;
				break;

			case "gametype":
				if(isdefined(temparr[i+1])) lastgt = temparr[i+1];
				i += 2;
				break;

			case "map":
				if(isdefined(temparr[i+1]))
				{
					xmaps[xmaps.size]["exec"] = lastexec;
					xmaps[xmaps.size-1]["gametype"] = lastgt;
					xmaps[xmaps.size-1]["map"] = temparr[i+1];
				}

				if(!random)
				{
					lastexec = undefined;
					lastgt = undefined;
				}

				i += 2;
				break;

			default:
				if(!level.ex_mapvote) iprintln(&"MAPROTATION_ERROR");

				if(isGametype(temparr[i])) lastgt = temparr[i];
				else if(isConfig(temparr[i])) lastexec = temparr[i];
				else
				{
					xmaps[xmaps.size]["exec"] = lastexec;
					xmaps[xmaps.size-1]["gametype"]	= lastgt;
					xmaps[xmaps.size-1]["map"]	= temparr[i];
	
					if(!random)
					{
						lastexec = undefined;
						lastgt = undefined;
					}
				}

				i += 1;
				break;
		}
	}

	if(random)
	{
		for(k = 0; k < 20; k++)
		{
			for(i = 0; i < xmaps.size; i++)
			{
				j = randomInt(xmaps.size);
				element = xmaps[i];
				xmaps[i] = xmaps[j];
				xmaps[j] = element;
			}
		}
	}

	return xmaps;
}

pbRotation()
{
	if(!level.ex_pbrotate) return;

	rot = pbNextMap();

	if(rot == "") return;

	currot = getcvar("sv_maprotationcurrent");
	newrot = rot + currot;
	setcvar("sv_maprotationcurrent", newrot);
}

pbNextMap()
{
	mapstring = undefined;
	execstring = undefined;
	gtstring = undefined;

	players = level.players;

	if(players.size >= 1)
	{
		setCvar("ex_playerslg", players.size);
		psize = players.size;
	}
	else psize = getCvarInt("ex_playerslg"); 

	if(psize >= level.ex_mapsizing_large)
	{
		map_rot_cur = "scr_large_rotation_current";
		map_rot = "scr_large_rotation";
	}
	else if(psize >= level.ex_mapsizing_medium)
	{
		map_rot_cur = "scr_med_rotation_current";
		map_rot = "scr_med_rotation";
	}
	else
	{
		map_rot_cur = "scr_small_rotation_current";
		map_rot = "scr_small_rotation";
	}
	
	cur_map_rot = getcvar(map_rot_cur);
	if(cur_map_rot == "" || cur_map_rot == " ")
	{
		setcvar(map_rot_cur, getcvar(map_rot) );
		cur_map_rot = getcvar(map_rot);
	}

	// parse rotation
	maprotation = splitArray(cur_map_rot, " ", "", true);
	for(i = 0; i < maprotation.size; i++ )
	{
		if(!isdefined(maprotation[i])) continue;

		if(maprotation[i] == "exec")
		{
			execstring = "exec " + maprotation[i+1] + " ";
			i++;
			continue;
		}
		else if(maprotation[i] == "gametype")
		{
			gtstring = "gametype " + maprotation[i+1] + " ";
			i++;
			continue;
		}
		else if(maprotation[i] == "map")
		{
			mapstring = "map " + maprotation[i+1] + " ";

			newcurrentstring = " ";
			for(a = i+2; a < maprotation.size; a++)
			{
				newcurrentstring = newcurrentstring + maprotation[a];
				newcurrentstring = newcurrentstring + " ";
			}

			if(newcurrentstring == " " || newcurrentstring == "  ") setcvar(map_rot_cur, getcvar(map_rot) );
			else setcvar(map_rot_cur, newcurrentstring);
			break;
		}
	}

	// rebuild rotation
	rebuild = "";
	if(!isdefined(mapstring)) return rebuild;
	if(isdefined(execstring)) rebuild = execstring;
	if(isdefined(gtstring)) rebuild = rebuild + gtstring;

	rebuild = rebuild + mapstring;
	return rebuild;
}

splitArray( str, sep, quote, skipEmpty )
{
	if(!isdefined(str) || str == "") return ( [] );

	if(!isdefined(sep) || sep == "") sep = ";";	// Default separator

	if(!isdefined(quote)) quote = "";

	skipEmpty = isdefined( skipEmpty );

	a = _splitRecur( 0, str, sep, quote, skipEmpty );

	return ( a );
}

_splitRecur( iter, str, sep, quote, skipEmpty )
{
	s = sep[ iter ];

	_a = [];
	_s = "";
	doQuote = false;
	for(i = 0; i < str.size; i++)
	{
		ch = str[i];
		if(ch == quote)
		{
			doQuote = !doQuote;

			if(iter + 1 < sep.size) _s += ch;
		}
		else
		if(ch == s && !doQuote )
		{
			if( _s != "" || !skipEmpty)
			{
				_l = _a.size;

				if(iter + 1 < sep.size)
				{
					_x = _splitRecur( iter + 1, _s,	sep, quote, skipEmpty );

					if(_x.size > 0 || !skipEmpty)
					{
						_a[ _l ][ "str" ] = _s;
						_a[ _l ][ "fields" ] = _x;
					}
				}
				else _a[ _l ] = _s;
			}

			_s = "";
		}
		else _s += ch;
	}

	if( _s != "")
	{
		_l = _a.size;

		if(iter + 1 < sep.size)
		{
			_x = _splitRecur( iter + 1, _s, sep, quote, skipEmpty);
			if(_x.size > 0 )
			{
				_a[ _l ][ "str" ] = _s;
				_a[ _l ][ "fields" ] = _x;
			}
		}
		else _a[ _l ] = _s;
	}

	return ( _a );
}

findStr( find, str, pos )
{
	if ( !isdefined( find ) || ( find == "" ) || !isdefined( str ) || !isdefined( pos ) || ( find.size > str.size ) )
		return ( -1 );

	fsize = find.size;
	ssize = str.size;

	switch ( pos )
	{
		case "start": place = 0 ; break;
		case "end":	place = ssize - fsize; break;
		default:	place = 0 ; break;
	}

	for ( i = place; i < ssize; i++ )
	{
		if ( i + fsize > ssize ) break; // Too late to compare

		// Compare now ...
		for ( j = 0; j < fsize; j++ )
			if ( str[ i + j ] != find[ j ] )
				break; // No match

		if ( j >= fsize ) return ( i ); // Found it!

		if ( pos == "start" ) break; // Didn't find at start
	}

	return ( -1 );
}

fixMapRotation()
{
	if(getcvar("sv_maprotation1") != "") return;

	maps = GetPlainMapRotation(false);
	if(!isdefined(maps) || !maps.size) return;

	newmaprotation = "";
	newmaprotationcurrent = "";
	for(i = 0; i < maps.size; i++)
	{
		if(!isdefined(maps[i]["exec"])) exec = "";
			else exec = " exec " + maps[i]["exec"];

		if(!isdefined(maps[i]["gametype"])) gametype = "";
			else gametype = " gametype " + maps[i]["gametype"];

		newmaprotation += exec + gametype + " map " + maps[i]["map"];

		if(i > 0) newmaprotationcurrent += exec + gametype + " map " + maps[i]["map"];
	}

	setCvar("sv_maprotation", strip(newmaprotation));
	setCvar("sv_maprotationcurrent", newmaprotationcurrent);
	setCvar("ex_fix_maprotation", "0");
}
	
randomMapRotation()
{
	if(level.ex_randommaprotation == 2 || level.ex_mapvote) return;

	maps = GetRandomMapRotation(false);
	if(!isdefined(maps) || !maps.size) return;

	lastexec = "";
	lastgt = "";

	newmaprotation = "";
	for(i = 0; i < maps.size; i++)
	{
		if(!isdefined(maps[i]["exec"]) || lastexec == maps[i]["exec"]) exec = "";
		else
		{
			lastexec = maps[i]["exec"];
			exec = " exec " + maps[i]["exec"];
		}

		if(!isdefined(maps[i]["gametype"]) || lastgt == maps[i]["gametype"]) gametype = "";
		else
		{
			lastgt = maps[i]["gametype"];
			gametype = " gametype " + maps[i]["gametype"];
		}

		newmaprotation += exec + gametype + " map " + maps[i]["map"];
	}

	setCvar("sv_maprotationcurrent", newmaprotation);
	setCvar("ex_random_maprotation", "2"); // do not set to 0; map rotation HUD message needs it
}

strip(s)
{
	if(s == "") return "";

	s2 = "";
	s3 = "";

	i = 0;
	while(i < s.size && s[i] == " ") i++;

	// String is just blanks?
	if(i == s.size) return "";
	
	for(; i < s.size; i++) s2 += s[i];

	i = s2.size - 1;
	while(s2[i] == " " && i > 0) i--;

	for(j = 0; j <= i; j++) s3 += s2[j];
		
	return s3;
}

isGametype(gt)
{
	switch(gt)
	{
		case "chq":
		case "cnq":
		case "ctf":
		case "ctfb":
		case "dm":
		case "dom":
		case "esd":
		case "ft":
		case "hm":
		case "hq":
		case "htf":
		case "ihtf":
		case "lib":
		case "lms":
		case "lts":
		case "ons":
		case "rbcnq":
		case "rbctf":
		case "sd":
		case "tdm":
		case "tkoth":
		case "vip":
			return true;

		default:
			return false;
	}
}

isConfig(cfg)
{
	temparr = explode(cfg, ".");
	if(temparr.size == 2 && temparr[1] == "cfg") return true;
		else return false;
}

explode(s, delimiter)
{
	j = 0;
	temparr[j] = "";	

	for(i = 0; i < s.size; i++)
	{
		if(s[i] == delimiter)
		{
			j++;
			temparr[j] = "";
		}
		else temparr[j] += s[i];
	}

	return temparr;
}
